

import java.util.*;
import java.util.stream.Collectors;

/**
 * 数组操作
 */
public class PracticsArrays {
    public static void main(String[] args) {
        /**
         * 补充：什么时候用基本数据类型，什么时候用包装数据类型
         *
         * 在以下情况应该优先考虑使用基本数据类型：
         *
         * 1.程序对性能要求很高。原因：基本数据类型的变量直接存储数值，不像包装类对象那样需要额外的内存空间来存储对象头信息以及进行垃圾回收。所以，在对性能要求很高或者需要处理大量数据的场景中，基本数据类型是更好的选择
         * 2.需要处理大量的数据。
         * 3.进行数值计算。原因：在进行数学运算时，基本数据类型能避免装箱和拆箱操作带来的性能损耗
         * 4.需要默认值。原因：基本数据类型有默认值，例如int的默认值是0，boolean的默认值是false。这在某些特定场景下非常有用
         * 5.要避免空指针异常。原因：基本数据类型不会出现null值，这能减少空指针异常的风险
         * 6.内存资源有限。原因：基本数据类型不会出现null值，这能减少空指针异常的风险
         *
         * 包装类则适用
         * 1.泛型
         * 2.需要null值表示
         * 3.与反射 API 交互
         */


        /**
         * 数组
         */
        /**
         * 数据里可以存放的内容
         * 1.数组里只能存放单一数据类型，一般为基础数据类型和Object。
         * 2.数组里可以存放的数据类型：int short double long     byte float boolean char  ，8种数据类型
         * 2.1.注意数组里可以存放Object，Object是包装类--而且此处的Object不是仅仅指数组里只放Object 而是数据里可以放所有的包装类的意思，
         * 例如：Integer、String、Boolean，不属于基本数据类型。
         * 3.每一种数组处理的方法都是对每种类型的数组进行单独处理。注意有些数据类型不能排序，所以排序的方法没有提供给某些数据类型，例如boolean
         * 4.数组是固定长度的。
         * 5.数组的单个内容可以修改。
         */


        //定义一维数组
        int[] nums1 = {2, 3, 1, 4};
        System.out.println(Arrays.toString(nums1));


        //定义二维数组
        int[][] nums2 = {{1, 2, 3}, {4, 5, 6, 7}};
        System.out.println(Arrays.toString(nums2[0]));
        System.out.println(Arrays.toString(nums2[1]));


        //定义数组
        int[] nums3 = new int[3];
        nums3[0] = 1;
        nums3[1] = 3;
        nums3[2] = 4;
        System.out.println(Arrays.toString(nums3));


        //更改数组内容
        int[] nums3_1 = {1, 2, 4, 4};
        nums3_1[2] = 8;
        System.out.println(Arrays.toString(nums3_1));


        //求数组长度-一维数据长度
        int[] nums4 = {2, 3, 1, 4, 5};
        System.out.println(nums4.length);

        //求数组长度-二维数据长度
        int[][] nums5 = {{1, 2, 3}, {4, 5, 6, 7}};
        System.out.println(nums5[1].length);
        System.out.println(nums5.length);

        //数组排序,默认升序排列，对7种基本数据类型（除了boolean）和Object都可以排序。
        int[] nums6 = {2, 3, 1, 4, 5};
        Arrays.sort(nums6);
        System.out.println(Arrays.toString(nums6));


        //搜索特定元素，返回元素坐标
        //用此方法的前提是数据必须排序，且数据里没有重复值，否则查出的结果每次不同。此方法也是每种数组都可以使用。
        int[] nums7 = {2, 3, 1, 4, 5};
        Arrays.sort(nums7);
        int index = Arrays.binarySearch(nums7, 2);
        System.out.println(index);

        //直接把数组转为string，此方法可以用于查看数组内容
        int[] nums8 = {2, 3, 1, 4, 5};
        System.out.println(Arrays.toString(nums8));


        //填充数组--全填充
        int[] nums9 = new int[3];
        Arrays.fill(nums9, 99);
        System.out.println(Arrays.toString(nums9));

        //填充数据-指定坐标进行填充
        int[] nums10 = new int[4];
        Arrays.fill(nums10, 0, 2, 99);
        System.out.println(Arrays.toString(nums10));


        //判断两个数组是否相等
        int[] nums11 = {1, 3, 2, 3};
        int[] nums12 = {1, 3, 2, 3};
        int[] nums13 = {1, 3};
        System.out.println(Arrays.equals(nums11, nums12));
        System.out.println(Arrays.equals(nums11, nums13));


        //复制数组
        int[] nums14 = {1, 3, 4, 3, 2};
        int[] nums15 = Arrays.copyOf(nums14, 5);
        System.out.println(Arrays.toString(nums15));

        //复制数组-2
        int[] nums16 = {5, 1, 6, 3, 2};
        int[] nums17 = Arrays.copyOfRange(nums16, 2, 4);//[2,4)注意是前闭后开区间。
        System.out.println(Arrays.toString(nums17));


        //数组转为List，且List是固定长度，不可改List长度和内容
        Integer[] nums17_1 = {5, 1, 6, 3, 2};
        List<Integer> list = Arrays.asList(nums17_1);//此方法传入的数组包含的内容必须是包装类，不能是基础数据类型。
        //如果是基础数据类型会出现装到list里只包含一个元素的情况。
        //list.add(3);//添加元素会报错 UnsupportedOperationException
        //list.remove(list.size()-1);//删除元素会报错


        //数组转为ArrayList，且ArrayList是可变长度，可以增加或删除元素
        Integer[] nums17_2 = {5, 1, 6, 3, 2};
        ArrayList<Integer> list2 = new ArrayList<>(Arrays.asList(nums17_2));//此方法传入的数组包含的内容必须是包装类，不能是基础数据类型。
        //如果是基础数据类型会出现装到list2里只包含一个元素的情况。
        list2.add(3);//可添加元素
        list2.remove(list2.size() - 1);//可删除元素


        //采用流的方式把数组放到list中,且List是可变长度，可改动List长度和内容；  java8以后的版本才可以使用stream
        Integer[] nums17_3 = {5, 1, 6, 3, 2};
        List<Integer> list3 = Arrays.stream(nums17_3).collect(Collectors.toList());
        list3.add(3);
        list3.remove(list3.size() - 1);


        //基本数据类型的数组转为list,用 stream，boxed
        int[] nums17_4 = {1, 3, 2, 1, 3};
        List<Integer> list4 = Arrays.stream(nums17_4).boxed().collect(Collectors.toList());


        //反转数组的方法
        int[] nums18 = {5, 1, 6, 3, 2};
        int[] nums19 = reverse(nums18);
        System.out.println(Arrays.toString(nums19));


        //数组获取最大值和最小值：先排序，第一个值就是最小值，最后一个值就是最大值
        int[] nums20 = {5, 1, 6, 3, 2};
        Arrays.sort(nums20);
        int min = nums20[0];
        int max = nums20[nums20.length - 1];
        System.out.println("最大值：" + max + " 最小值：" + min);


        //合并数组-自定义方法1
        int[] nums21 = {2, 5, 0};
        int[] nums22 = {3, 1, 4};
        System.out.println(Arrays.toString(joinArrays(nums21, nums22)));


        //合并数组-自定义方法2
        int[] nums21_1 = {2, 5, 0};
        int[] nums22_1 = {3, 1, 4};
        System.out.println(Arrays.toString(joinArrays2(nums21_1, nums22_1)));


        //数组扩容--方法1
        int[] nums23 = {2, 5, 7};//要扩容两个值，值为:4，8
        int[] nums24 = new int[5];
        nums6[3] = 4;
        nums6[4] = 8;
        System.arraycopy(nums23, 0, nums24, 0, nums23.length);
        System.out.println(Arrays.toString(nums24));

        //数组扩容--方法2
        int[] nums23_1 = {2, 5, 7};//要扩容两个值，值为:4，8
        int[] nums24_1 =Arrays.copyOf(nums23_1,5);
        nums24_1[3]=4;
        nums24_1[4]=8;
        System.out.println(Arrays.toString(nums24_1));



        //打印数组中重复元素-自定义方法
        int[] nums25 = {1, 2, 5, 5, 6, 6, 7, 2, 9, 2};
        findDupicateInArray(nums25);


        //删除数组中指定位置的元素-自定义方法1
        int[] nums26 = {2, 3, 1, 3, 4};
        int[] nums27 = deleteFromIndex(nums26, 1);
        System.out.println(Arrays.toString(nums27));


        //删除数组中指定位置的元素-自定义方法2
        int[] nums26_1 = {2, 3, 1, 3, 4};
        int[] nums27_2 = deleteFromIndex(nums26_1, 1);
        System.out.println(Arrays.toString(nums27_2));


        //合并两个数组
        int[] nums28 = {1, 3, 2, 3};
        int[] nums29 = {1, 5};
        System.out.println(Arrays.toString(union(nums28, nums29)));


    }


    //反转数组-自定义
    public static int[] reverse(int[] nums) {
        int length = nums.length;
        int[] dis = new int[length];
        for (int i = 0; i < length; i++) {
            dis[i] = nums[length - i - 1];
        }
        return dis;
    }


    //合并数组-不使用List-自定义
    public static int[] joinArrays(int[] nums1, int[] nums2) {
        int size1 = nums1.length;
        int size2 = nums2.length;
        int size = size1 + size2;

        int[] dis = new int[size];

        for (int i = 0; i < size1; i++) {
            dis[i] = nums1[i];
        }

        for (int i = 0; i < size2; i++) {
            dis[i + size1] = nums2[i];
        }
        return dis;
    }

    //合并数组-使用list
    public static int[] joinArrays2(int[] nums1, int[] nums2) {
        int size1 = nums1.length;
        int size2 = nums2.length;
        int size = size1 + size2;
        int[] res = new int[size];

        ArrayList<Integer> list = new ArrayList<>();

        for (int i = 0; i < size1; i++) {
            list.add(nums1[i]);
        }

        for (int i = 0; i < size2; i++) {
            list.add(nums2[i]);
        }

        for (int i = 0; i < list.size(); i++) {
            res[i] = ((Integer) list.get(i)).intValue();
        }

        return res;
    }


    //查找数组中重复元素-自定义
    public static void findDupicateInArray(int[] nums) {
        Arrays.sort(nums);
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] == nums[j]) {
                    count++;
                }
            }
            if (count == 1) {//一圈中只取最后一个重复值，前面有重复的不取
                System.out.println("重复元素 : " + nums[i]);
            }
            count = 0;//对比一圈就置零---必须放这个位置
        }

    }


    //删除数组中的元素-不用arraylist--自定义
    public static int[] deleteFromIndex(int[] nums, int index) {
        if (index < 0 || index > nums.length) {
            System.out.println("无法删除对应坐标的元素");
            return null;
        }
        int[] newNums = new int[nums.length - 1];
        for (int i = 0; i < nums.length - 1; i++) {//因为后面要加1，所以此处少算一个值
            if (i < index) {
                newNums[i] = nums[i];
            } else {
                newNums[i] = nums[i + 1];
            }
        }
        return newNums;

    }


    //删除数组中的元素-使用arraylist
    public static int[] deleteFromIndex2(int[] nums, int index) {
        if (index < 0 || index > nums.length) {
            System.out.println("无法删除对应坐标的元素");
            return null;
        }
        int[] newNums = new int[nums.length - 1];

        List<Integer> list = Arrays.stream(nums).boxed().collect(Collectors.toList());

        list.remove(index);
        for (int i = 0; i < list.size(); i++) {
            newNums[i] = ((Integer) list.get(i)).intValue();
        }

        return newNums;

    }

    //求两个数组的并集，且去除重复值。支持用set--自定义
    public static int[] union(int[] nums1, int[] nums2) {
        Set<Integer> set = new HashSet<>();

        for (int n : nums1) {
            set.add(n);

        }
        for (int m : nums2) {
            set.add(m);

        }

        Object[] res = new Object[set.size()];
        res = set.toArray();
        int[] result = new int[res.length];
        for (int i = 0; i < res.length; i++) {
            result[i] = Integer.valueOf(String.valueOf(res[i]));
        }
        return result;
    }
    }

